using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace BepuUtilities.Memory
{
    public static class SpanHelper
    {
        /// <summary>
        /// 最大大小跨度指数。最大跨度为2^Maximumspan SizePower。这避免了溢出。
        /// </summary>
        public const int MaximumSpanSizePower = 30;

        [Conditional("DEBUG")]
        internal static void ValidatePower(int power)
        {
            Debug.Assert(power >= 0 && power <= MaximumSpanSizePower, $"Power must be from 0 to {MaximumSpanSizePower}, inclusive.");
        }
        /// <summary>
        /// 计算最大整数N,使2^N小于或等于i。
        /// </summary>
        /// <param name="i">要计算的幂的整数。</param>
        /// <return>设置整数N下限,使2^N小于或等于i。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int GetPowerOf2(int i)
        {
            int log = 0;
            if ((i & 0xFFFF0000) > 0)
            {
                i >>= 16;
                log |= 16;
            }
            if ((i & 0xFF00) > 0)
            {
                i >>= 8;
                log |= 8;
            }
            if ((i & 0xF0) > 0)
            {
                i >>= 4;
                log |= 4;
            }
            if ((i & 0xC) > 0)
            {
                i >>= 2;
                log |= 2;
            }
            if ((i & 0x2) > 0)
            {
                log |= 1;
            }
            return log;
        }
        /// <summary>
        /// 计算最小整数N,使2^N>=i。
        /// </summary>
        /// <param name="i">要计算的幂的整数。</param>
        /// <return>设置下限整数N,使2^N>=i.</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static int GetContainingPowerOf2(int i)
        {
            Debug.Assert(i >= 0 && i <= (1 << MaximumSpanSizePower), "i must be from 0 to " + ((1 << MaximumSpanSizePower) - 1) + ", inclusive.");
            // 我们需要完全包含计数的缓冲区,因此它应该是有效的上限(Log(I))。
            // 将值加倍(并减去1,以避免已经是2的幂的情况)就可以解决这个问题。
            i = ((i > 0 ? i : 1) << 1) - 1;
            return GetPowerOf2(i);
        }

        /// <summary>
        /// 测试泛型参数是否为基元参数。捷径;专门的编译。
        /// </summary>
        /// <typeparam name="T">键入以检查原语。</typeparam>
        /// <return>如果类型是基元类型之一,则为True,否则为False。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool IsPrimitive<T>()
        {
            // jit能够专门化这一点,因此整个函数只是变成一个常量。
            return
                typeof(T) == typeof(bool) ||
                typeof(T) == typeof(byte) ||
                typeof(T) == typeof(sbyte) ||
                typeof(T) == typeof(ushort) ||
                typeof(T) == typeof(short) ||
                typeof(T) == typeof(uint) ||
                typeof(T) == typeof(int) ||
                typeof(T) == typeof(ulong) ||
                typeof(T) == typeof(long) ||
                typeof(T) == typeof(IntPtr) ||
                typeof(T) == typeof(UIntPtr) ||
                typeof(T) == typeof(char) ||
                typeof(T) == typeof(double) ||
                typeof(T) == typeof(float);
        }

        /// <summary>
        /// 测试类型是否为基元类型。慢速路径;非专门化编译。
        /// </summary>
        /// <param name="type">键入以检查原语。</typeparam>
        /// <return>如果类型是基元类型之一,则为True,否则为False。</return>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool IsPrimitive(Type type)
        {
            // 日本不能专门化这个！如果没有值类型泛型参数,则jit不会生成不同的版本。
            return
                type == typeof(bool) ||
                type == typeof(byte) ||
                type == typeof(sbyte) ||
                type == typeof(ushort) ||
                type == typeof(short) ||
                type == typeof(uint) ||
                type == typeof(int) ||
                type == typeof(ulong) ||
                type == typeof(long) ||
                type == typeof(IntPtr) ||
                type == typeof(UIntPtr) ||
                type == typeof(char) ||
                type == typeof(double) ||
                type == typeof(float);
        }


        /// <summary>
        /// 将数据从一个缓冲区复制到另一个缓冲区。
        /// </summary>
        /// <typeparam name="T">要复制的元素的类型。</typeparam>
        /// <param name="source">要从中拉取元素的源缓冲区。</param>
        /// <param name="sourceIndex">开始从中拉取元素的缓冲区中的索引。</param>
        /// <param name="target">要设置值的目标缓冲区。</param>
        /// <param name="targetIndex">缓冲区中开始将元素放入的索引。</param>
        /// <param name="count">要复制的元素数。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe void Copy<T>(in Buffer<T> source, int sourceIndex, in Buffer<T> target, int targetIndex, int count) where T : unmanaged
        {
            Debug.Assert(targetIndex >= 0 && targetIndex + count <= target.Length, "Can't perform a copy that extends beyond the target span.");
            Debug.Assert(sourceIndex >= 0 && sourceIndex + count <= source.Length, "Can't perform a copy that extends beyond the source span.");
            Unsafe.CopyBlockUnaligned(target.Memory + targetIndex, source.Memory + sourceIndex, (uint)(Unsafe.SizeOf<T>() * count));
        }

        /// <summary>
        /// 将数据从缓冲区复制到范围。
        /// </summary>
        /// <typeparam name="T">要复制的元素的类型。</typeparam>
        /// <param name="source">要从中拉取元素的源缓冲区。</param>
        /// <param name="sourceIndex">开始从中拉取元素的缓冲区中的索引。</param>
        /// <param name="target">要设置值的目标范围。</param>
        /// <param name="targetIndex">开始将元素放入的范围中的索引。</param>
        /// <param name="count">要复制的元素数。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe void Copy<T>(in Buffer<T> source, int sourceIndex, Span<T> target, int targetIndex, int count) where T : unmanaged
        {
            Debug.Assert(targetIndex >= 0 && targetIndex + count <= target.Length, "Can't perform a copy that extends beyond the target span.");
            Debug.Assert(sourceIndex >= 0 && sourceIndex + count <= source.Length, "Can't perform a copy that extends beyond the source span.");
            Unsafe.CopyBlockUnaligned(ref Unsafe.As<T, byte>(ref target[targetIndex]), ref Unsafe.AsRef<byte>(source.Memory + sourceIndex), (uint)(Unsafe.SizeOf<T>() * count));
        }

        /// <summary>
        /// 将数据从跨区复制到缓冲区。
        /// </summary>
        /// <typeparam name="T">要复制的元素的类型。</typeparam>
        /// <param name="source">要从中拉取元素的源范围。</param>
        /// <param name="sourceIndex">开始从中拉取元素的范围中的索引。</param>
        /// <param name="target">要设置值的目标缓冲区。</param>
        /// <param name="targetIndex">缓冲区中开始将元素放入的索引。</param>
        /// <param name="count">要复制的元素数。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe void Copy<T>(in Span<T> source, int sourceIndex, in Buffer<T> target, int targetIndex, int count) where T : unmanaged
        {
            Debug.Assert(targetIndex >= 0 && targetIndex + count <= target.Length, "Can't perform a copy that extends beyond the target span.");
            Debug.Assert(sourceIndex >= 0 && sourceIndex + count <= source.Length, "Can't perform a copy that extends beyond the source span.");
            Unsafe.CopyBlockUnaligned(ref Unsafe.AsRef<byte>(target.Memory + targetIndex), ref Unsafe.As<T, byte>(ref source[sourceIndex]), (uint)(Unsafe.SizeOf<T>() * count));
        }

        /// <summary>
        /// 将数据从跨区复制到缓冲区。
        /// </summary>
        /// <typeparam name="T">要复制的元素的类型。</typeparam>
        /// <param name="source">要从中拉取元素的源范围。</param>
        /// <param name="sourceIndex">开始从中拉取元素的范围中的索引。</param>
        /// <param name="target">要设置值的目标缓冲区。</param>
        /// <param name="targetIndex">缓冲区中开始将元素放入的索引。</param>
        /// <param name="count">要复制的元素数。</param>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe void Copy<T>(in ReadOnlySpan<T> source, int sourceIndex, in Buffer<T> target, int targetIndex, int count) where T : unmanaged
        {
            Debug.Assert(targetIndex >= 0 && targetIndex + count <= target.Length, "Can't perform a copy that extends beyond the target span.");
            Debug.Assert(sourceIndex >= 0 && sourceIndex + count <= source.Length, "Can't perform a copy that extends beyond the source span.");
            source.Slice(sourceIndex, count).CopyTo(new Span<T>(target.Memory + targetIndex, count));
        }
    }
}
